package com.typhoon.spring_shiro.dao.ha;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.jdbc.core.JdbcOperations;


/**
 * 高可用DB从库HealthCheck Task
 *
 * @author zhangshsh 2016-02-22 作成
 */
public class HABalanceJdbcHealthCheckTask implements Runnable {
    /**
     * LOG
     */
    private static Logger log = Logger
            .getLogger(HABalanceJdbcHealthCheckTask.class);

    /**
     * DB从库Jdbc列表
     */
    private List<JdbcOperations> crmB_N_JdbcList;

    /**
     * Node健康值Bean列表
     */
    private ArrayList<NodeHealthValueBean> healthyValueList = new ArrayList<NodeHealthValueBean>();

    /**
     * 运行中标志
     */
    private volatile boolean runFlag = true;

    /**
     * 健康值 PropertyChangeSupport
     */
    protected PropertyChangeSupport listeners = new PropertyChangeSupport(this);

    public HABalanceJdbcHealthCheckTask(
            List<JdbcOperations> crmB_N_JdbcList) {
        // DB从库Jdbc列表
        this.crmB_N_JdbcList = crmB_N_JdbcList;

        int                 len      = this.crmB_N_JdbcList.size();
        NodeHealthValueBean nodeBean = null;
        for (int i = 0; i < len; i++) {
            nodeBean = new NodeHealthValueBean();
            nodeBean.setNodeIndex(i);
            // 初始状态为正常
            nodeBean.setHealthValue(0);
            // add
            this.healthyValueList.add(nodeBean);
        }
    }

    /**
     * 添加 健康值PropertyChangeListener
     *
     * @param listener
     */
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        this.listeners.addPropertyChangeListener(listener);
    }

    /**
     * 移除 健康值PropertyChangeListener
     *
     * @param listener
     */
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        this.listeners.removePropertyChangeListener(listener);
    }

    /**
     * 取得健康值
     *
     * @param jdbcOpers
     * @return -1: 表示已经Down; 0-4级;
     */
    private int getHealthyValue(JdbcOperations jdbcOpers) {
        int result = 0;
        try {
            long beforeMS = (new Date()).getTime();
            // query
            jdbcOpers.queryForInt("select 1");
            long afterMS = (new Date()).getTime();

            // diff
            long diff = afterMS - beforeMS;

            // log
            // log.warn("diff=" + diff);
            if (diff >= 0 && diff <= 500) {
                // 执行时长0-500ms内为正常
                result = 0;
            } else if (diff > 500 && diff <= 1000) {
                // 执行时长500-1000ms内为降1级
                result = 1;
            } else if (diff > 1000 && diff <= 3000) {
                result = 2;
            } else if (diff > 3000 && diff <= 5000) {
                result = 3;
            } else if (diff > 5000) {
                result = 4;
            }
        } catch (Exception e) {
            result = -1;
        }

        return result;
    }

    /**
     * 检测所有Node的健康值
     */
    public void run() {
        while (this.runFlag) {
            try {
                int                 len          = this.crmB_N_JdbcList.size();
                JdbcOperations      jdbcOpers    = null;
                NodeHealthValueBean nodeBean     = null;
                int                 healthyValue = -1;
                for (int i = 0; i < len; i++) {
                    jdbcOpers = this.crmB_N_JdbcList.get(i);
                    //
                    healthyValue = getHealthyValue(jdbcOpers);
                    nodeBean = this.healthyValueList.get(i);

                    if (nodeBean.getHealthValue() != healthyValue) {
                        NodeHealthValueBean oldValue = new NodeHealthValueBean();
                        oldValue.setNodeIndex(i);
                        oldValue.setHealthValue(nodeBean.getHealthValue());

                        NodeHealthValueBean newValue = new NodeHealthValueBean();
                        newValue.setNodeIndex(i);
                        newValue.setHealthValue(healthyValue);

                        PropertyChangeEvent event = new PropertyChangeEvent(
                                this, "healthyValue", oldValue, newValue);
                        // fire 健康值变化event
                        this.listeners.firePropertyChange(event);

                        // set new healthvalue
                        nodeBean.setHealthValue(healthyValue);
                        // set lastUpdate
                        nodeBean.setLastUpdate(new Date());
                    }
                }
            } catch (Exception e) {
                log.warn(e.getMessage());
            }

            try {
                // sleep 5 秒
                Thread.sleep(5000);
            } catch (Exception e) {
                // ignore error
            }
        }
    }

}
